CAN Bus Adapter
The CAN Bus Adapter is a collection of tools that lets you develop and integrate tests and simulations involving communication over a CAN bus within the miniHIL platform.
The set of tools includes:
-
Runtime and model support libraries
-
Easy sending and receiving can frames to the physical can bus from any eTrice actor
-
Supports Remote Transmission Requests (RTR)
-
Supports Extended CAN Frames
-
-
An DBC-to-eTrice model generator
-
Automatically creates ROOM ActorClasses that support automatic encoding, decoding, packing and unpacking of can frame signals
-
The CAN generator is part of the gradle build. It is part of the normal assemble process. If needed the generator can also be invoked separately: .\gradlew.bat generateDBCConf
. The gradle task generates room models for all files with .dbcconf
extension in the model/model-user folder in the miniHilProject as well as in all configured library projects.
CAN HAL
Currently, two separate CAN instances with a pair of physical pins on the MiniHiL board each, are available.
The sample point is currently fixed at 75%. If this leads to problems with high CAN-FD bitrates talk to us. |
Structure
Structure and dependencies of the can actors
How to use the HAL
-
Connect your DuT to the MiniHiL board. For blue miniHIL boards (v2.3 and higher) have a look at the pinout below. For older boards the following warning is important.
On old (pre V2.3, green) miniHIL boards the pinout of the CAN SUB-D connector is mirrored. I.e. the following pins are swapped: 1 <→ 5, 2 <→ 4, 3 <→ 3, 6 <→ 9, 7 <→ 8. |
-
Import the CAN actor.
import can.api.CANService.*
import can.platform.Stm32CANService.*
-
Create an instance of either AStm32CANService1 or AStm32CANService2 (or both) and connect the ports.
ActorRef can1: AStm32CANService1
ActorRef can2: AStm32CANService2
-
Initialize the CAN actor by sending an open(DCANParams) message to the CAN service ctl port. Make sure both nominal and data bitrate match with the DuT.
DCanParams params = {
.baudRate_kbs = 40,
.baudRate_kbs_fd = 125
};
// CANctl bound to ctl port of CAN service actor
CANctl.open(¶ms);
Both baudRate_kbs and baudRate_kbs_fd params must be set to valid values. Otherwise the initialization of the CAN service will fail and it will respond to the open request with a ctl.errorOpen notification message. |
-
Wait until you receive the opened() message in response.
-
You are now able to send and receive CAN messages through the CANData port.
CANMessage msg;
msg.id = 0x00;
msg.isExtendedFrame = false;
//Length has to be a value of {1,2,3,4,5,6,7,8,12,16,20,24,32}
msg.length = 16;
msg.data[0] = 0x00;
msg.data[1] = 0x01;
//...
msg.data[15] = 0x0f;
msg.isRemoteFrame = false;
msg.isBitRateSwitched = true;
msg.isFDFrame = true;
CANdata.send(&msg);
Example Application (HAL only)
This Application will initialize CAN and send one Message per second while waiting for incoming Messages.
RoomModel MiniHilProject {
import can.api.CANService.*
import can.platform.Stm32CANService.*
import etrice.api.timer.PTimer
ActorClass Application {
Structure {
conjugated Port CANctl: PCANCtrl
conjugated Port CANdata: PCANData
ActorRef can1: AStm32CANService1
ActorRef can2: AStm32CANService2
Binding CANdata and can1.bus
Binding CANctl and can1.ctl
SAP timer: PTimer
}
Behavior {
StateMachine {
State Active {
}
State Initializing
State FatalError
Transition init0: initial -> Initializing {
action '''
//initialize CAN with Nominal Bitrate = 40kb and Data Bitrate = 125kb
DCanParams params = {
.baudRate_kbs = 40,
.baudRate_kbs_fd = 125
};
CANctl.open(¶ms);
'''
}
Transition tr0: Initializing -> Active {
triggers {
<opened: CANctl>
}
action '''
timer.startTimer(1000);
'''
}
Transition tr1: Initializing -> FatalError {
triggers {
<errorOpen: CANctl>
}
action '''
etLogger_logError("Failed to initialize CANServiceTest!");
'''
}
Transition tr2: Active -> Active {
triggers {
<received: CANdata>
}
action '''
// received CAN Message
uint8 * d = transitionData->data;
'''
}
Transition tr3: Active -> Active {
triggers {
<errorRecv: CANctl>
}
action '''// received incorrect CAN Message'''
}
Transition tr4: Active -> Active {
triggers {
<errorSend: CANctl>
}
action '''// sending CAN Message failed'''
}
Transition tr5: Active -> Active {
triggers {
<timeout: timer>
}
action '''
// send CAN Message on timer timeout
CANMessage msg;
msg.id = 0x00;
msg.isExtendedFrame = 0;
//Length has to be a value of {1,2,3,4,5,6,7,8,12,16,20,24,32}
msg.length = 16;
msg.data[0] = 0x00;
msg.data[1] = 0x01;
//...
msg.data[15] = 0x0f;
msg.isRemoteFrame = 0;
msg.isBitRateSwitched = true;
msg.isFDFrame = true;
CANdata.send(&msg);
'''
}
}
}
}
}
Ingress filtering CAN messages by frame id
If many actors are connected to the HAL canBus port each actor will receive a copy of the received can message. This might lead to performance issues. But, in many cases each connected actor only reacts to can messages with specific ids. In those cases ingress filtering can be used.
The PCANData
Protocol supports message filtering for each connected user port. To enable filtering you have to first stop forwarding of all message by sending a disableForwarding
message. Afterwards you may configure which messages your actor should receive using the enableForwardingWithFilter
message with DCanFilterConfig
payload. NOTE that the DCanFilterConfig
only takes a pointer to a uint32
array. To cater for different filter needs (number of valid ids) the array of valid canIDs must be kept within the users actor. The PortClass directly accesses that array and does not create a copy.